﻿/* ================================================================================
<copyright file="LogEventItem.cs" company="The Framework Factory, LLC" lastWriteTime="12/05/2012 23:23:47 PM">
  Copyright (c) 2012 The Framework Factory, LLC.  All rights reserved.
</copyright>
=================================================================================== */
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Xml;
using System.Xml.XPath;

using FWF.Logging.Xml;

using log4net;
using log4net.Core;
using log4net.Util;

namespace FWF.Logging
{
    /// <summary>
    /// 
    /// </summary>
    public class LogEventItem : IXmlSerialize
    {

        #region Constructors / Finalizer

        /// <summary>
        /// Initializes a new instance of the <see cref="LogEventItem"/> class.
        /// </summary>
        public LogEventItem()
        {
            Properties = new Dictionary<string, string>();
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the id.
        /// </summary>
        /// <value>The id.</value>
        public long Id
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the name of the machine.
        /// </summary>
        /// <value>The name of the machine.</value>
        public string MachineName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the name of the instance.
        /// </summary>
        /// <value>The name of the instance.</value>
        public string InstanceName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the name of the logger.
        /// </summary>
        /// <value>The name of the logger.</value>
        public string LoggerName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the time stamp.
        /// </summary>
        /// <value>The time stamp.</value>
        public DateTime TimeStamp
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the level value.
        /// </summary>
        /// <value>The level value.</value>
        public int LevelValue
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the level.
        /// </summary>
        /// <value>The level.</value>
        public string LevelName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the session id.
        /// </summary>
        /// <value>The session id.</value>
        public int SessionId
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the name of the session.
        /// </summary>
        /// <value>The name of the session.</value>
        public string SessionName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the session context.
        /// </summary>
        /// <value>The session context.</value>
        public string SessionContext
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the name of the class.
        /// </summary>
        /// <value>The name of the class.</value>
        public string ClassName
        {
            get;
            set;
        }
        
        /// <summary>
        /// Gets or sets the name of the file.
        /// </summary>
        /// <value>The name of the file.</value>
        public string FileName
        {
            get;
            set;
        }
        
        /// <summary>
        /// Gets or sets the line number.
        /// </summary>
        /// <value>The line number.</value>
        public int LineNumber
        {
            get;
            set;
        }
        
        /// <summary>
        /// Gets or sets the name of the method.
        /// </summary>
        /// <value>The name of the method.</value>
        public string MethodName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the message.
        /// </summary>
        /// <value>The message.</value>
        public string Message
        {
            get;
            set;
        }
        
        /// <summary>
        /// Gets or sets the type of the exception.
        /// </summary>
        /// <value>The type of the exception.</value>
        public string ExceptionType
        {
            get;
            set;
        }
        
        /// <summary>
        /// Gets or sets the exception message.
        /// </summary>
        /// <value>The exception message.</value>
        public string ExceptionMessage
        {
            get;
            set;
        }
        
        /// <summary>
        /// Gets or sets the name of the thread.
        /// </summary>
        /// <value>The name of the thread.</value>
        public string ThreadName
        {
            get;
            set;
        }
        
        /// <summary>
        /// Gets or sets the name of the user.
        /// </summary>
        /// <value>The name of the user.</value>
        public string UserName
        {
            get;
            set;
        }
        
        /// <summary>
        /// Gets or sets the identity.
        /// </summary>
        /// <value>The identity.</value>
        public string Identity
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the name of the app domain.
        /// </summary>
        /// <value>
        /// The name of the app domain.
        /// </value>
        public string AppDomainName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the properties.
        /// </summary>
        /// <value>The properties.</value>
        public IDictionary<string, string> Properties
        {
            get;
            set;
        }

        #endregion

        #region IXmlSerialize Members

        /// <summary>
        /// Writes the current instance as Xml
        /// </summary>
        /// <param name="writer">The writer.</param>
        public void ToXml( XmlWriter writer )
        {
            writer.WriteStartElement( XmlStatic.XmlPrefix, "LogEventItem", XmlStatic.XmlNamespace );

            writer.WriteAttributeString( XmlStatic.XmlPrefix, "id", XmlStatic.XmlNamespace, Id.ToString( CultureInfo.CurrentCulture ) );
            writer.WriteAttributeString( XmlStatic.XmlPrefix, "dateTime", XmlStatic.XmlNamespace, TimeStamp.ToString( CultureInfo.CurrentCulture ) );
            writer.WriteAttributeString( XmlStatic.XmlPrefix, "machineName", XmlStatic.XmlNamespace, MachineName );
            writer.WriteAttributeString( XmlStatic.XmlPrefix, "instanceName", XmlStatic.XmlNamespace, InstanceName );

            writer.WriteAttributeString( XmlStatic.XmlPrefix, "loggerName", XmlStatic.XmlNamespace, LoggerName );
            writer.WriteAttributeString( XmlStatic.XmlPrefix, "levelValue", XmlStatic.XmlNamespace, LevelValue.ToString( CultureInfo.CurrentCulture ) );
            writer.WriteAttributeString( XmlStatic.XmlPrefix, "levelName", XmlStatic.XmlNamespace, LevelName );
            writer.WriteAttributeString( XmlStatic.XmlPrefix, "sessionId", XmlStatic.XmlNamespace, SessionId.ToString( CultureInfo.CurrentCulture ) );
            writer.WriteAttributeString( XmlStatic.XmlPrefix, "sessionName", XmlStatic.XmlNamespace, SessionName );
            writer.WriteAttributeString( XmlStatic.XmlPrefix, "sessionContext", XmlStatic.XmlNamespace, SessionContext );

            writer.WriteAttributeString( XmlStatic.XmlPrefix, "className", XmlStatic.XmlNamespace, ClassName );
            writer.WriteAttributeString( XmlStatic.XmlPrefix, "methodName", XmlStatic.XmlNamespace, MethodName );
            writer.WriteAttributeString( XmlStatic.XmlPrefix, "fileName", XmlStatic.XmlNamespace, FileName );
            writer.WriteAttributeString( XmlStatic.XmlPrefix, "lineNumber", XmlStatic.XmlNamespace, LineNumber.ToString( CultureInfo.CurrentCulture ) );

            writer.WriteAttributeString( XmlStatic.XmlPrefix, "exceptionType", XmlStatic.XmlNamespace, ExceptionType );
            writer.WriteAttributeString( XmlStatic.XmlPrefix, "threadName", XmlStatic.XmlNamespace, ThreadName );
            writer.WriteAttributeString( XmlStatic.XmlPrefix, "userName", XmlStatic.XmlNamespace, UserName );
            writer.WriteAttributeString( XmlStatic.XmlPrefix, "identity", XmlStatic.XmlNamespace, Identity );

            writer.WriteStartElement( XmlStatic.XmlPrefix, "message", XmlStatic.XmlNamespace );
            writer.WriteCData( Message );
            writer.WriteEndElement();

            writer.WriteStartElement( XmlStatic.XmlPrefix, "exception", XmlStatic.XmlNamespace );
            writer.WriteCData( ExceptionMessage );
            writer.WriteEndElement();

            if ( Properties != null )
            {
                writer.WriteStartElement( XmlStatic.XmlPrefix, "properties", XmlStatic.XmlNamespace );

                foreach (string key in Properties.Keys)
                {
                    writer.WriteElementString( key, XmlStatic.XmlNamespace, Properties[ key ] );
                }

                writer.WriteEndElement();
            }

            writer.WriteEndElement();
        }

        /// <summary>
        /// Populates the current instance from Xml
        /// </summary>
        /// <param name="nav">The nav.</param>
        /// <exception cref="System.InvalidOperationException">XPathNavigator is located on an incorrect node</exception>
        public void FromXml( XPathNavigator nav )
        {
            if ( nav.LocalName != "LogEventItem" )
            {
                throw new InvalidOperationException( "XPathNavigator is located on an incorrect node" );
            }

            Id = Int64.Parse( nav.GetAttribute( "id", XmlStatic.XmlNamespace ) );
            TimeStamp = DateTime.Parse( nav.GetAttribute( "dateTime", XmlStatic.XmlNamespace ) );
            MachineName = nav.GetAttribute( "machineName", XmlStatic.XmlNamespace );
            InstanceName = nav.GetAttribute( "instanceName", XmlStatic.XmlNamespace );

            LoggerName = nav.GetAttribute( "loggerName", XmlStatic.XmlNamespace );
            LevelValue = Int32.Parse( nav.GetAttribute( "levelValue", XmlStatic.XmlNamespace ) );
            LevelName = nav.GetAttribute( "levelName", XmlStatic.XmlNamespace );

            SessionId = Int32.Parse( nav.GetAttribute( "sessionId", XmlStatic.XmlNamespace ) );
            SessionName = nav.GetAttribute( "sessionName", XmlStatic.XmlNamespace );
            SessionContext = nav.GetAttribute( "sessionContext", XmlStatic.XmlNamespace );

            ClassName = nav.GetAttribute( "className", XmlStatic.XmlNamespace );
            MethodName = nav.GetAttribute( "methodName", XmlStatic.XmlNamespace );
            FileName = nav.GetAttribute( "fileName", XmlStatic.XmlNamespace );
            LineNumber = Int32.Parse( nav.GetAttribute( "lineNumber", XmlStatic.XmlNamespace ) );

            ExceptionType = nav.GetAttribute( "exceptionType", XmlStatic.XmlNamespace );
            ThreadName = nav.GetAttribute( "threadName", XmlStatic.XmlNamespace );
            UserName = nav.GetAttribute( "userName", XmlStatic.XmlNamespace );
            Identity = nav.GetAttribute( "identity", XmlStatic.XmlNamespace );

            if ( nav.MoveToChild( "message", XmlStatic.XmlNamespace ) )
            {
                Message = nav.Value;
                nav.MoveToParent();
            }

            if ( nav.MoveToChild( "exception", XmlStatic.XmlNamespace ) )
            {
                ExceptionMessage = nav.Value;
                nav.MoveToParent();
            }

            if ( nav.MoveToChild( "properties", XmlStatic.XmlNamespace ) )
            {
                Properties.Clear();

                XPathNodeIterator xDocChildren = nav.SelectChildren( XPathNodeType.Element );

                foreach (XPathNavigator xDocChild in xDocChildren)
                {
                    string name = xDocChild.LocalName;
                    string value = xDocChild.Value;

                    Properties.Add( name, value );
                }

                nav.MoveToParent();
            }
        }

        #endregion

        /// <summary>
        /// Populates the data table.
        /// </summary>
        /// <param name="dataTable">The data table.</param>
        public static void PopulateDataTable( DataTable dataTable )
        {
            dataTable.Columns.Add( "Id", typeof( long ) );
            dataTable.Columns.Add( "TimeStamp", typeof( DateTime ) );
            dataTable.Columns.Add( "MachineName", typeof( string ) );
            dataTable.Columns.Add( "InstanceName", typeof( string ) );
            dataTable.Columns.Add( "LoggerName", typeof( string ) );
            dataTable.Columns.Add( "LevelValue", typeof( string ) );
            dataTable.Columns.Add( "LevelName", typeof( string ) );
            dataTable.Columns.Add( "SessionId", typeof( int ) );
            dataTable.Columns.Add( "SessionName", typeof( string ) );
            dataTable.Columns.Add( "SessionContext", typeof( string ) );
            dataTable.Columns.Add( "ClassName", typeof( string ) );
            dataTable.Columns.Add( "FileName", typeof( string ) );
            dataTable.Columns.Add( "LineNumber", typeof( int ) );
            dataTable.Columns.Add( "MethodName", typeof( string ) );
            dataTable.Columns.Add( "Message", typeof( string ) );
            dataTable.Columns.Add( "ExceptionType", typeof( string ) );
            dataTable.Columns.Add( "ExceptionMessage", typeof( string ) );
            dataTable.Columns.Add( "ThreadName", typeof( string ) );
            dataTable.Columns.Add( "UserName", typeof( string ) );

            dataTable.Columns.Add( "Identity", typeof ( string ) );
        }

        /// <summary>
        /// Froms the logging event data.
        /// </summary>
        /// <param name="loggingEventData">The logging event data.</param>
        /// <returns></returns>
        public static LogEventItem FromLoggingEventData( LoggingEventData loggingEventData )
        {
            var itm = new LogEventItem();

            itm.Id = -1;
            itm.TimeStamp = loggingEventData.TimeStamp;
            itm.LoggerName = loggingEventData.LoggerName;
            itm.LevelValue = loggingEventData.Level.Value;
            itm.LevelName = loggingEventData.Level.Name;
            itm.ClassName = loggingEventData.LocationInfo.ClassName;
            itm.FileName = loggingEventData.LocationInfo.FileName;
            itm.MethodName = loggingEventData.LocationInfo.MethodName;
            itm.Message = loggingEventData.Message;
            itm.ThreadName = loggingEventData.ThreadName;
            itm.UserName = loggingEventData.UserName;
            itm.Identity = loggingEventData.Identity;
            itm.AppDomainName = loggingEventData.Domain;
            itm.ExceptionMessage = loggingEventData.ExceptionString;

            //
            if ( !string.IsNullOrEmpty( loggingEventData.LocationInfo.LineNumber ) )
            {
                itm.LineNumber = Convert.ToInt32( loggingEventData.LocationInfo.LineNumber );
            }

            //
            if ( loggingEventData.Properties != null )
            {
                itm.MachineName = loggingEventData.Properties[ "machineName" ] as string;
                itm.InstanceName = loggingEventData.Properties[ "instanceName" ] as string;
                itm.SessionName = loggingEventData.Properties[ "sessionName" ] as string;
                itm.SessionContext = loggingEventData.Properties[ "sessionContext" ] as string;
                itm.ExceptionType = loggingEventData.Properties[ "exceptionType" ] as string;

                if ( loggingEventData.Properties.Contains( "id" ) )
                {
                    itm.Id = Convert.ToInt64( loggingEventData.Properties[ "id" ] );
                }
                if ( loggingEventData.Properties.Contains( "sessionId" ) )
                {
                    itm.SessionId = Convert.ToInt32( loggingEventData.Properties["sessionId"] );
                }
            }

            //
            if ( string.IsNullOrEmpty( itm.InstanceName ) )
            {
                itm.InstanceName = GlobalContext.Properties["instanceName"] as string;
            }
            if ( string.IsNullOrEmpty( itm.SessionName ) )
            {
                itm.SessionName = LogicalThreadContext.Properties["sessionName"] as string;
            }
            if ( string.IsNullOrEmpty( itm.SessionContext ) )
            {
                itm.SessionContext = LogicalThreadContext.Properties["sessionContext"] as string;
            }
            if ( itm.SessionId == -1 )
            {
                object obj = LogicalThreadContext.Properties[ "sessionId" ];

                if ( !ReferenceEquals( obj, null ) )
                {
                    itm.SessionId = Convert.ToInt32( obj );
                }
            }

            return itm;
        }

        /// <summary>
        /// To the logging event.
        /// </summary>
        /// <returns></returns>
        public LoggingEventData ToLoggingEventData()
        {
            var loggingEventData = new LoggingEventData();

            loggingEventData.TimeStamp = TimeStamp;
            loggingEventData.LoggerName = LoggerName;
            loggingEventData.Level = new Level( LevelValue, LevelName );
            loggingEventData.LocationInfo = new LocationInfo( ClassName, MethodName, FileName, LineNumber.ToString( CultureInfo.CurrentCulture ) );
            loggingEventData.Message = Message;
            loggingEventData.ThreadName = ThreadName;
            loggingEventData.UserName = UserName;
            loggingEventData.ExceptionString = ExceptionMessage;
            loggingEventData.Identity = Identity;
            loggingEventData.Domain = AppDomainName;

            loggingEventData.Properties = new PropertiesDictionary();
            loggingEventData.Properties[ "id" ] = Id.ToString( CultureInfo.CurrentCulture );
            loggingEventData.Properties[ "machineName" ] = MachineName;
            loggingEventData.Properties[ "instanceName" ] = InstanceName;
            loggingEventData.Properties[ "sessionName" ] = SessionName;
            loggingEventData.Properties[ "sessionContext" ] = SessionContext;
            loggingEventData.Properties[ "exceptionType" ] = ExceptionType;
            loggingEventData.Properties[ "sessionId" ] = SessionId;

            return loggingEventData;
        }

        /// <summary>
        /// Creates a <see cref="DataRow"/> record for a <see cref="DataTable"/> and 
        /// populates the <see cref="DataRow"/> with the current instance.
        /// </summary>
        /// <param name="dataTable">The data table.</param>
        /// <returns></returns>
        public DataRow ToDataRow( DataTable dataTable )
        {
            DataRow row = dataTable.NewRow();

            row.ItemArray = new object[] {
                Id,
                TimeStamp,
                MachineName,
                InstanceName,
                LoggerName,
                LevelValue,
                LevelName,
                SessionId,
                SessionName,
                SessionContext,
                ClassName,
                FileName,
                LineNumber,
                MethodName,
                Message,
                ExceptionType,
                ExceptionMessage,
                ThreadName,
                UserName,
                Identity
            };

            return row;
        }

        /// <summary>
        /// Creates a new <see cref="LogEventItem"/> instance and populates it from a <see cref="DataRow"/>.
        /// </summary>
        /// <param name="row">The row.</param>
        /// <returns></returns>
        public static LogEventItem FromDataRow( DataRow row )
        {
            var itm = new LogEventItem();

            itm.Id = (long) row.ItemArray[ 0 ];
            itm.TimeStamp = (DateTime) row.ItemArray[ 1 ];
            itm.MachineName = row.ItemArray[ 2 ] as string;
            itm.InstanceName = row.ItemArray[ 3 ] as string;
            itm.LoggerName = row.ItemArray[ 4 ] as string;
            itm.LevelValue = Convert.ToInt32( row.ItemArray[ 5 ] );
            itm.LevelName = row.ItemArray[ 6 ] as string;
            itm.SessionId = Convert.ToInt32( row.ItemArray[ 7 ] );
            itm.SessionName = row.ItemArray[ 8 ] as string;
            itm.SessionContext = row.ItemArray[ 9 ] as string;
            itm.ClassName = row.ItemArray[ 10 ] as string;
            itm.FileName = row.ItemArray[ 11 ] as string;
            itm.LineNumber = Convert.ToInt32( row.ItemArray[ 12 ] );
            itm.MethodName = row.ItemArray[ 13 ] as string;
            itm.Message = row.ItemArray[ 14 ] as string;
            itm.ExceptionType = row.ItemArray[ 15 ] as string;
            itm.ExceptionMessage = row.ItemArray[ 16 ] as string;
            itm.ThreadName = row.ItemArray[ 17 ] as string;
            itm.UserName = row.ItemArray[ 18 ] as string;
            itm.Identity = row.ItemArray[19] as string;

            return itm;
        }

    }
}
